Skip to content

使用CSS中的网格布局

CSS 网格布局(grid布局)引入了二维网格布局系统,可用于布局页面主要的区域或小型组件。是CSS3提供的新特性,和flex布局一样,灵活强大、可以优雅地实现各种复杂的布局效果。在业务开发中尝试去使用,取代之前的float布局,让代码更整洁,同时性能更优异。

兼容性

先来看一下 CSS Grid Layout 在各种浏览器上的兼容性吧。

Alt text

妥妥的没有问题,就连IE都在一定程度上支持了。基本上在生产环境也可以安心使用了。

网格布局的基本概念

什么是网格?

网格是一组相交的水平线和垂直线,它定义了网格的列和行。我们可以将网格元素放置在与这些行和列相关的位置上。 这样我们就能很容易的将元素放置在合适的位置,并且提供了灵活的对齐方式。

Alt text

在一个二维的布局空间中,设定了行和列,来实现网格布局。

网格轨道

一个网格轨道就是网格中任意两条线之间的空间。

Alt text

下面给一个wrapper容器,设置为display: grid,让它成为网格布局的容器。 设置 grid-template-columns属性,添加列轨道,并定义列轨道的宽为 200px。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}
.wrapper {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}

添加了三列等宽的列轨道

Alt text

fr单位

轨道可以使用任何长度单位进行定义。网格中还引入了一个另外的长度单位来帮助我们创建灵活的网格轨道。新的fr单位代表网格容器中可用空间的一等份。

css
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

创建三列等宽的轨道,这些轨道会随着容器可用空间的大小弹性增长和收缩。

Alt text

定义大小不等分的轨道

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
}
.wrapper {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
}

空间被四等分,第一个占据2份的空间,后面的两个各占据一份的空间。

Alt text

混合尺寸

css
.wrapper {
  display: grid;
  grid-template-columns: 500px 1fr 2fr;
}
.wrapper {
  display: grid;
  grid-template-columns: 500px 1fr 2fr;
}

第一个轨道占据 500px的宽,容器剩下的空间三等分,第二个空间占据一份,第三个空间占据两份。

Alt text

使用repeat()标记重复部分

有多个轨道占据着同样的空间大小,可以使用 repeat()简化代码

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr); 
  /* grid-template-columns: 1fr 1fr 1fr; */
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr); 
  /* grid-template-columns: 1fr 1fr 1fr; */
}

grid-template-columns: repeat(3, 1fr) === grid-template-columns: 1fr 1fr 1fr;

以上两种写法等价。

repeat函数也可以用于控制重复轨道列表中的一部分。起始轨道为 20 像素,接着重复了 6 个1fr的轨道,最后再添加了一个 20 像素的轨道

css
.wrapper {
  display: grid;
  grid-template-columns: 20px repeat(6, 1fr) 20px;
}
.wrapper {
  display: grid;
  grid-template-columns: 20px repeat(6, 1fr) 20px;
}

多轨道模式的重复。网格共计有 10 个轨道,为 1 个1fr轨道后面跟着 1 个2fr轨道,该模式重复 5 次。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(5, 1fr 2fr);
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(5, 1fr 2fr);
}

隐式和显式网格

上面我们仅用用 grid-template-columns 属性定义了列轨道,没有定义行轨道,这些行轨道会被创建在隐式网格中。 这些隐式创建的行轨道网格,会根据元素内容的大小自动定义尺寸。

Alt text

可以在隐式网格中用 grid-auto-rows 和 grid-auto-columns 属性来定义一个设置大小尺寸的轨道。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 200px;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 200px;
}

这样隐式创建的行轨道的大小为 200px高。

Alt text

minmax()设定轨道大小

设置一个显式的网格或者定义自动创建的行和列的大小的时候,可以使用 minmax()指定空间范围,实现弹性伸缩。

html
<div class="wrapper">
  <div>One</div>
  <div>
    Two
    <p>I have some more content in.</p>
    <p>This makes me taller than 100 pixels.</p>
  </div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
<div class="wrapper">
  <div>One</div>
  <div>
    Two
    <p>I have some more content in.</p>
    <p>This makes me taller than 100 pixels.</p>
  </div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(100px, auto);
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(100px, auto);
}

行高将会是最小 100 像素,最大为 auto。意味着行的尺寸将会根据内容的大小来自动变换。根据本行中最高的单元,把空间扩展到足够容纳该单元。

Alt text

网格线

定义网格时,我们定义的是网格轨道,而不是网格线。grid 会自动创建带编号的网格线来定位每一个网格元素。例如下面这个三列两行的网格中,就拥有四条纵向的网格线。

网格线的开始位置是从 1开始的,而不是 0 。

Alt text

同时,我们可以跨轨道设定要放置的区域。这时就要用到网格线来定位了。

html
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
  <div class="box5">Five</div>
</div>
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
  <div class="box5">Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}
.box2 {
  grid-column-start: 1;
  grid-row-start: 3;
  grid-row-end: 5;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}
.box2 {
  grid-column-start: 1;
  grid-row-start: 3;
  grid-row-end: 5;
}

Alt text

网格单元

和表格中的一个单元格很像,网格单元是一个网格元素中最小的单位。一旦父级容器定义了网格布局(display: grid;),那么它的子级元素将会排列在每个事先定义好的网格单元中。在下面的图中,将第一个网格单元作高亮处理。

Alt text

网格区域

网格容器中的子元素可以占据多个网格单元,向行或着列的方向扩展一个或多个单元,并且会创建一个网格区域。

Alt text

网格区域只能是矩形,无法创建出一个 L 型的网格区域。

网格间距

在两个网格单元之间, 网格横向间距 或 网格纵向间距可使用 grid-column-gapgrid-row-gap 属性来表示,或者直接使用两个合并的缩写形式 grid-gap

创建一个横向间距为 10px、纵向间距为 1em 的网格元素。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 1em;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 1em;
}

Alt text

嵌套网格

一个网格中的子元素又可以成为一个网格容器,形成网格嵌套网格的布局。

css
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

Alt text

展示层级

多个子元素可能会占用网格中的同一个单元格,这样就会出现子元素重叠的现象。

html
<div class="wrapper">
  <div class="box box1">One</div>
  <div class="box box2">Two</div>
  <div class="box box3">Three</div>
  <div class="box box4">Four</div>
  <div class="box box5">Five</div>
</div>
<div class="wrapper">
  <div class="box box1">One</div>
  <div class="box box2">Two</div>
  <div class="box box3">Three</div>
  <div class="box box4">Four</div>
  <div class="box box5">Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}
.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}
.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
}

网格项目 box2 现在覆盖于 box1 之上,其覆盖顺序遵循文档流的原始顺序(后来居上)。

Alt text

有点儿类似于绝对定位的元素,在Z轴上的层叠堆放,同样也使用z-index 属性控制重叠的展示顺序。

css
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  z-index: 2;
}
.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
  z-index: 1;
}
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  z-index: 2;
}
.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
  z-index: 1;
}

由于给 box2 设定一个低于 box1 的 z-index 值,box2 将会显示在 box1 的下方。

Alt text

基于网格线的定位

网格编号线是网格布局的开始,使用网格布局时,编号线总是存在。这些线从 1 开始按行和列编号。要注意的是网格是根据书写方向来编号的,像中文都是从左向右书写,网格线的 1 是最左边的线。当使用从右至左书写的语言时,网格线 1 就是最右边的线。

使用线编号定位元素

使用编号线定位元素在网格上的位置,比如想要第一个元素从网格的最左开始,占 1 个列导轨。它还应该从第 1 行线开始,延伸至第 4 行线。

html
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
</div>
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
</div>
css
.box1 {
  grid-column-start: 1;
  grid-column-end: 2;
  grid-row-start: 1;
  grid-row-end: 4;
}
.box2 {
  grid-column-start: 3;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}
.box3 {
  grid-column-start: 2;
  grid-column-end: 3;
  grid-row-start: 1;
  grid-row-end: 2;
}
.box4 {
  grid-column-start: 2;
  grid-column-end: 4;
  grid-row-start: 3;
  grid-row-end: 4;
}
.box1 {
  grid-column-start: 1;
  grid-column-end: 2;
  grid-row-start: 1;
  grid-row-end: 4;
}
.box2 {
  grid-column-start: 3;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}
.box3 {
  grid-column-start: 2;
  grid-column-end: 3;
  grid-row-start: 1;
  grid-row-end: 2;
}
.box4 {
  grid-column-start: 2;
  grid-column-end: 4;
  grid-row-start: 3;
  grid-row-end: 4;
}

逐个定义元素后,我们把所有 4 个元素都填充到了网格中。这里的 grid-column-start: 1;grid-column-end: 2; 表示横向上选取网格线【1,2】之间的区域,grid-row-start: 1;grid-row-end: 3;表示在纵向上选取网格线【1,4】之间的区域。

Alt text

grid-column 和 grid-row 的简写

每个子元素都定义 grid-column-startgrid-column-endgrid-row-startgrid-row-end 这样的属性不免有些繁琐,提供了 grid-columngrid-row的简写形式。

css
.box1 {
  /* 相当于:grid-column-start: 1;grid-column-end: 2; */
  grid-column: 1 / 2;
  /* 相当于:grid-row-start: 1; grid-row-end: 4;*/
  grid-row: 1 / 4;
}
.box2 {
  grid-column: 3 / 4;
  grid-row: 1 / 3;
}
.box3 {
  grid-column: 2 / 3;
  grid-row: 1 / 2;
}
.box4 {
  grid-column: 2 / 4;
  grid-row: 3 / 4;
}
.box1 {
  /* 相当于:grid-column-start: 1;grid-column-end: 2; */
  grid-column: 1 / 2;
  /* 相当于:grid-row-start: 1; grid-row-end: 4;*/
  grid-row: 1 / 4;
}
.box2 {
  grid-column: 3 / 4;
  grid-row: 1 / 3;
}
.box3 {
  grid-column: 2 / 3;
  grid-row: 1 / 2;
}
.box4 {
  grid-column: 2 / 4;
  grid-row: 3 / 4;
}

实现的效果和上面的示例等同。

默认跨度

在上例中,为了展示这些属性,我们指定了每个结束行线和列线,但实际上如果一个元素只延伸一个轨道的话,可以省略 grid-column-end 或 grid-row-end 值。元素默认延伸一个轨道。

css
.box1 {
  grid-column-start: 1;
  /* 默认 grid-column-end: 2;*/
  grid-row-start: 1;
  grid-row-end: 4;
}
.box2 {
  grid-column-start: 3;
  /* 默认 grid-column-end: 4;*/
  grid-row-start: 1;
  grid-row-end: 3;
}
.box3 {
  grid-column-start: 2;
  /* 默认 grid-column-end: 3;*/
  grid-row-start: 1;
  /* 默认 grid-row-end: 2; */
}
.box4 {
  grid-column-start: 2;
  grid-column-end: 4;
  grid-row-start: 3;
  /* 默认 grid-row-end: 4; */
}
.box1 {
  grid-column-start: 1;
  /* 默认 grid-column-end: 2;*/
  grid-row-start: 1;
  grid-row-end: 4;
}
.box2 {
  grid-column-start: 3;
  /* 默认 grid-column-end: 4;*/
  grid-row-start: 1;
  grid-row-end: 3;
}
.box3 {
  grid-column-start: 2;
  /* 默认 grid-column-end: 3;*/
  grid-row-start: 1;
  /* 默认 grid-row-end: 2; */
}
.box4 {
  grid-column-start: 2;
  grid-column-end: 4;
  grid-row-start: 3;
  /* 默认 grid-row-end: 4; */
}

形成的效果,和上面的示例等同。

默认跨度的简写

可以省略跨越一个轨道的元素的斜杠和第二个值。

css
.box1 {
  /* 针对只跨越一个轨道的情况,直接省略了 / 和第二个值 */
  grid-column: 1;
  grid-row: 1 / 4;
}
.box2 {
  grid-column: 3;
  grid-row: 1 / 3;
}
.box3 {
  grid-column: 2;
  grid-row: 1;
}
.box4 {
  grid-column: 2 / 4;
  grid-row: 3;
}
.box1 {
  /* 针对只跨越一个轨道的情况,直接省略了 / 和第二个值 */
  grid-column: 1;
  grid-row: 1 / 4;
}
.box2 {
  grid-column: 3;
  grid-row: 1 / 3;
}
.box3 {
  grid-column: 2;
  grid-row: 1;
}
.box4 {
  grid-column: 2 / 4;
  grid-row: 3;
}

形成的效果,和上面的示例等同。

grid-area 属性

可以进一步精简,只给每个子元素定义一个属性grid-area,值的顺序如下。

  • grid-row-start
  • grid-column-start
  • grid-row-end
  • grid-column-end
css
.box1 {
  grid-area: 1 / 1 / 4 / 2;
}
.box2 {
  grid-area: 1 / 3 / 3 / 4;
}
.box3 {
  grid-area: 1 / 2 / 2 / 3;
}
.box4 {
  grid-area: 3 / 2 / 4 / 4;
}
.box1 {
  grid-area: 1 / 1 / 4 / 2;
}
.box2 {
  grid-area: 1 / 3 / 3 / 4;
}
.box3 {
  grid-area: 1 / 2 / 2 / 3;
}
.box4 {
  grid-area: 3 / 2 / 4 / 4;
}

形成的效果,和上面的示例等同。

Alt text

grid-area 的值的顺序看起来可能有点奇怪,比如说它正好和定义 margin 和 padding 的简写的值的顺序相反。这是因为它与 CSS 书写模式规范中的书写方向相关。有 4 个书写流关联的方向:

  • 块起始(block-start)
  • 块结束(block-end)
  • 行起始(inline-start)
  • 行结束(inline-end)

这里我们不深入探讨了,

反方向计数

也可以从行和列结束线开始反方向计数,对于中文来说就是右端的列线和底端的行线。这些线会被记为 -1,然后从这往前数,所以倒数第 2 条线会被记为 -2。值得注意的是最后一条线是指显式定义网格的最后一条线,即由 grid-template-columnsgrid-template-rows 定义的网格,并不把隐式定义网格所加入的行和列纳入考虑。

html
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
</div>
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
</div>
css
.box1 {
  grid-column-start: -1;
  grid-column-end: -2;
  grid-row-start: -1;
  grid-row-end: -4;
}
.box2 {
  grid-column-start: -3;
  grid-column-end: -4;
  grid-row-start: -1;
  grid-row-end: -3;
}
.box3 {
  grid-column-start: -2;
  grid-column-end: -3;
  grid-row-start: -1;
  grid-row-end: -2;
}
.box4 {
  grid-column-start: -2;
  grid-column-end: -4;
  grid-row-start: -3;
  grid-row-end: -4;
}
.box1 {
  grid-column-start: -1;
  grid-column-end: -2;
  grid-row-start: -1;
  grid-row-end: -4;
}
.box2 {
  grid-column-start: -3;
  grid-column-end: -4;
  grid-row-start: -1;
  grid-row-end: -3;
}
.box3 {
  grid-column-start: -2;
  grid-column-end: -3;
  grid-row-start: -1;
  grid-row-end: -2;
}
.box4 {
  grid-column-start: -2;
  grid-column-end: -4;
  grid-row-start: -3;
  grid-row-end: -4;
}

通过从右端和底端开始定义布局,把之前的示例的布局翻转了。

Alt text

间距

CSS 网格规范加入了用 column-gap 和 row-gap 属性定义列间距和行间距的能力。

间距只出现在网格轨道与轨道之间,它们并不会出现在网格容器的四周。通过在网格容器上定义这些属性,我们给上例加上间距:

html
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
</div>
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
</div>
css
.box1 {
  grid-column: 1;
  grid-row: 1 / 4;
}
.box2 {
  grid-column: 3;
  grid-row: 1 / 3;
}
.box3 {
  grid-column: 2;
  grid-row: 1;
}
.box4 {
  grid-column: 2 / 4;
  grid-row: 3;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 100px);
  /* 增加列间距 */
  column-gap: 20px;
  /* 增加行间距 */
  row-gap: 1em;
}
.box1 {
  grid-column: 1;
  grid-row: 1 / 4;
}
.box2 {
  grid-column: 3;
  grid-row: 1 / 3;
}
.box3 {
  grid-column: 2;
  grid-row: 1;
}
.box4 {
  grid-column: 2 / 4;
  grid-row: 3;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 100px);
  /* 增加列间距 */
  column-gap: 20px;
  /* 增加行间距 */
  row-gap: 1em;
}

Alt text

网格间距简写

column-gaprow-gap 这两个属性可以用 gap 简写。如果只给出一个值,那这个值会同时应用于行间距和列间距。如果给了两个值,第一个会被用于 row-gap,第二个则会被用于 column-gap。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 100px);
  /*等价于 row-gap: 1em;column-gap: 20px; */
  gap: 1em 20px;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 100px);
  /*等价于 row-gap: 1em;column-gap: 20px; */
  gap: 1em 20px;
}

从基于线定位的角度来说,间距的行为就像是使基线变得特别宽。所有从这条线开始的东西会从间距结束的地方开始,并不能定位也不能放任何东西到这个间距的空间中。

span 关键字

除了”起始线与结束线“的定位方法,我们还可以使用”起始线与跨越轨道数量“的定位方法

css
.box1 {
  grid-column: 1;
  /* 从行1号线开始,然后跨越 3 个轨道结束 */
  grid-row: 1 / span 3;
}
.box2 {
  grid-column: 3;
  /* 从行1号线开始,然后跨越 2 个轨道结束 */
  grid-row: 1 / span 2;
}
.box3 {
  grid-column: 2;
  grid-row: 1;
}
.box4 {
  /* 从列 2 号线开始,然后跨越 2 个轨道结束 */
  grid-column: 2 / span 2;
  grid-row: 3;
}
.box1 {
  grid-column: 1;
  /* 从行1号线开始,然后跨越 3 个轨道结束 */
  grid-row: 1 / span 3;
}
.box2 {
  grid-column: 3;
  /* 从行1号线开始,然后跨越 2 个轨道结束 */
  grid-row: 1 / span 2;
}
.box3 {
  grid-column: 2;
  grid-row: 1;
}
.box4 {
  /* 从列 2 号线开始,然后跨越 2 个轨道结束 */
  grid-column: 2 / span 2;
  grid-row: 3;
}

Alt text

也可以在 grid-row-start/grid-row-endgrid-column-start/grid-column-end 属性中使用 span 关键字。

设定起始行,然后在结束线位置跨越 3 条线。那这个元素就会从 1 号线开始,跨越 3 条线,到 4 号线结束。

css
.box1 {
  grid-column-start: 1;
  /* 等同于 grid-row: 1 / span 3; */
  grid-row-start: 1;
  grid-row-end: span 3;
}
.box1 {
  grid-column-start: 1;
  /* 等同于 grid-row: 1 / span 3; */
  grid-row-start: 1;
  grid-row-end: span 3;
}

设置起始线为跨越 3 条线。意味着这个元素会从指定的线往上跨越 3 条线。这个元素会从 4 号线开始,跨越 3 条线到 1 号线。

css
.box1 {
  grid-column-start: 1;
  grid-row-start: span 3;
  grid-row-end: 4;
}
.box1 {
  grid-column-start: 1;
  grid-row-start: span 3;
  grid-row-end: 4;
}

网格模板区域

在使用 CSS 网格布局时,因为离不开网格线,所以最直接的方式就是使用网格线来定位项目。不过,还有另一种替代方法用于定位项目,可以独立使用它,也可把它和基于网格线的定位结合起来。这就是采用对模板区域命名的方式来定位项目。

命名网格区域

我们已经接触过 grid-area 属性了,它把网格线的 4 个属性值合为 1 个值,用于定位一个网格区域。

css
.box1 {
  grid-area: 1 / 1 / 4 / 2;
}
.box1 {
  grid-area: 1 / 1 / 4 / 2;
}

在用网格线定义网格区域时,我们是通过指定围绕网格区域的四条线来定义的。

Alt text

我们也可以先给一个区域命名,然后在 grid-template-areas 属性值中指定这个区域的位置。可以随意为区域命名,比如,如果要创建下面的布局,可以先划分出 4 个主要的区域:

  • 头部(header)
  • 尾部(footer)
  • 侧边栏(sidebar)
  • 主要内容(content)

Alt text

通过 grid-area 属性为这些区域各分配一个名字,这不会影响布局。这样就有了用于布局的命名过的区域了。

css
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}

有了这些名字,接下来就可以创建布局了。此前我们是通过在项目自身上指定线序号来定位项目,而现在,则要把完整的布局都写在网格容器中。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    "ft ft ft ft   ft   ft   ft   ft   ft";
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    "ft ft ft ft   ft   ft   ft   ft   ft";
}
html
<div class="wrapper">
  <div class="header">Header</div>
  <div class="sidebar">Sidebar</div>
  <div class="content">Content</div>
  <div class="footer">Footer</div>
</div>
<div class="wrapper">
  <div class="header">Header</div>
  <div class="sidebar">Sidebar</div>
  <div class="content">Content</div>
  <div class="footer">Footer</div>
</div>

Alt text

用这种方法,我们就不需要为任何网格项目单独指定属性,所有的定义都在网格容器中了。

留出空白的网格单元

在上面的例子中,已经实现了用区域填充网格,不留空余空间,我们也可以用这种布局方法留出空的网格单元。留出空单元的方法是使用句点符,“.”。假如想把尾部区域仅显示在主要内容的下方,就应该让侧边栏下面的三个单元格为空。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    ".  .  .  ft   ft   ft   ft   ft   ft";
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    ".  .  .  ft   ft   ft   ft   ft   ft";
}

Alt text

为了让布局更整齐,可以使用多个“.”符号,如果在多个句点符号之间没有空格,那它们就会被计为一个单元格。用多个句点表示一个单元格的好处是对于复杂的布局来说很容易让行列对齐,这意味着你在 CSS 中看到的,就像是实际布局看起来那样。

跨越多个网格单元

在上面的例子中,每个区域都跨越了多个网格单元,为了实现这个效果,要把区域名字在它的区域内重复写多次,中间用空格分隔。也可以在 grid-template-areas 中添加额外的空格以便让列对齐。

要让侧边栏向下跨到底部,只需要用 sd 标识替换掉 '.' 符号就可以了。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    "sd sd sd  ft  ft   ft   ft   ft   ft";
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    "sd sd sd  ft  ft   ft   ft   ft   ft";
}

Alt text

grid-template-areas 的值必须是一个完整的网格,否则无效(即这个属性会被忽略掉),这意味着你应该让每一行都有相同数量的单元格,如果出现句点符就表示这个单元格将被留空。如果创建的区域不是矩形,也是无效的网格。

结合用媒体查询

(通过媒体查询)在 CSS 的多个不同地方修改它也非常容易。我们可以重新定义网格、重新定位网格中的项目、甚至完全改变它。

若要(通过媒体查询)重新定义网格,应该把区域名称定义在媒体查询之外,在这种情况下,主要内容区域就总是会被称为 main,不管它被(媒体查询语句)定位在网格中的什么位置。

对于上面的例子,我们可能希望在较窄的宽度下使用非常简单的布局,网格只定义一个列,所有的项目从上到下排列。

css
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}

.wrapper {
  display: grid;
  grid-auto-rows: minmax(100px, auto);
  grid-template-columns: 1fr;
  grid-template-areas:
    "hd"
    "main"
    "sd"
    "ft";
}
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}

.wrapper {
  display: grid;
  grid-auto-rows: minmax(100px, auto);
  grid-template-columns: 1fr;
  grid-template-areas:
    "hd"
    "main"
    "sd"
    "ft";
}

把目前的布局放到媒体查询里,并且调整为当容器空间变得足够宽时,把目前两列的布局改为三列的布局。注意对于宽布局仍保留 9 列轨道,而只是在 grid-template-areas 里重新定义了项目的位置。

css
/* 500 ~ 700px之间时 */
@media (min-width: 500px) {
  .wrapper {
    grid-template-columns: repeat(9, 1fr);
    grid-template-areas:
      "hd hd hd hd   hd   hd   hd   hd   hd"
      "sd sd sd main main main main main main"
      "sd sd sd  ft  ft   ft   ft   ft   ft";
  }
}
/* 大于 700px时 */
@media (min-width: 700px) {
  .wrapper {
    grid-template-areas:
      "hd hd hd   hd   hd   hd   hd   hd hd"
      "sd sd main main main main main ft ft";
  }
}
/* 500 ~ 700px之间时 */
@media (min-width: 500px) {
  .wrapper {
    grid-template-columns: repeat(9, 1fr);
    grid-template-areas:
      "hd hd hd hd   hd   hd   hd   hd   hd"
      "sd sd sd main main main main main main"
      "sd sd sd  ft  ft   ft   ft   ft   ft";
  }
}
/* 大于 700px时 */
@media (min-width: 700px) {
  .wrapper {
    grid-template-areas:
      "hd hd hd   hd   hd   hd   hd   hd hd"
      "sd sd main main main main main ft ft";
  }
}

Alt text

在UI元素上使用grid-template-areas

网上许多例子都使用网格来做页面的整体布局,实际上网格对于小元素一样有用。使用 grid-template-areas,可以很轻松实现想要的效果。

定义一个两个列轨道的网格,图片列的尺寸是 1fr,文本列的尺寸是 3fr。如果你想让图片区域占据固定的宽度,就可以把图片列的尺寸设定一个像素值,把文本区域的尺寸设定为 1fr。只有一个 1fr 的列轨道的意思是让它占据剩余的空间。

把图片区域命名为 img,把文本区域命名为 content,然后把它们摆放在 grid-template-areas 属性中。

css
* {
  box-sizing: border-box;
}

.media {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  max-width: 400px;
}
.media {
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-template-areas: "img content";
  margin-bottom: 1em;
}

.media .image {
  grid-area: img;
  background-color: #ffd8a8;
}

.media .text {
  grid-area: content;
  padding: 10px;
}
* {
  box-sizing: border-box;
}

.media {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  max-width: 400px;
}
.media {
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-template-areas: "img content";
  margin-bottom: 1em;
}

.media .image {
  grid-area: img;
  background-color: #ffd8a8;
}

.media .text {
  grid-area: content;
  padding: 10px;
}
html
<div class="media">
  <div class="image"></div>
  <div class="text">
    This is a media object example. We can use grid-template-areas to switch
    around the image and text part of the media object.
  </div>
</div>
<div class="media">
  <div class="image"></div>
  <div class="text">
    This is a media object example. We can use grid-template-areas to switch
    around the image and text part of the media object.
  </div>
</div>

Alt text

调换位置

把图片显示在盒子的另一侧,为了实现这个效果,只需要把网格的 1fr 轨道放到后边,把 grid-template-areas 里的值简单地调换个位置即可

css
.media.flipped {
  grid-template-columns: 3fr 1fr;
  grid-template-areas: "content img";
}
.media.flipped {
  grid-template-columns: 3fr 1fr;
  grid-template-areas: "content img";
}

Alt text

网格定义的简写规则

简写方式不仅能够一口气把多个属性缩写成一行,它们还会把一些你没有设置过的值、或不能在简写中设置的值重置成初始值。所以如果要用简写方式,一定要意识到它可能会把你在别处已经设置过的值给重置了。

用于网格容器的简写方式,分别是显式网格简写 grid-template,和网格定义简写 grid

grid-template

grid-template 属性可同时设置以下属性:

  • grid-template-rows
  • grid-template-columns
  • grid-template-areas

这个属性之所以被称为显式网格简写,是因为它设置的都是在定义显式网格时要用到的属性,而不是那些与创建隐式的行或列轨道相关的属性。

下面的代码使用 grid-template 创建了一个布局,效果与本文前面创建的布局是一样的。

css
.wrapper {
  display: grid;
  grid-template:
    "hd hd hd hd   hd   hd   hd   hd   hd" minmax(100px, auto)
    "sd sd sd main main main main main main" minmax(100px, auto)
    "ft ft ft ft   ft   ft   ft   ft   ft" minmax(100px, auto)
    / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}
.wrapper {
  display: grid;
  grid-template:
    "hd hd hd hd   hd   hd   hd   hd   hd" minmax(100px, auto)
    "sd sd sd main main main main main main" minmax(100px, auto)
    "ft ft ft ft   ft   ft   ft   ft   ft" minmax(100px, auto)
    / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

第一个值是 grid-template-areas 的值,并且在每一行的末尾声明了行的大小,也就是 minmax(100px, auto) 的作用。

在 grid-template-areas 之后用一个左斜杠分隔,再之后是一个详细的列轨道清单。

grid

grid 简写方式更进一步,它包含了与隐式网格相关的属性,所以通过它可以同时设置以下属性:

  • grid-template-rows
  • grid-template-columns
  • grid-template-areas
  • grid-auto-rows
  • grid-auto-columns
  • grid-auto-flow

这个属性会把 grid-gap 属性的值重置为 0,而且你还不能在简写中设置间距值。

可以像使用 grid-template 一样使用这个语法,不过要注意的是当它执行时,它会重置一些其他值。

css
.wrapper {
  display: grid;
  grid:
    "hd hd hd hd   hd   hd   hd   hd   hd" minmax(100px, auto)
    "sd sd sd main main main main main main" minmax(100px, auto)
    "ft ft ft ft   ft   ft   ft   ft   ft" minmax(100px, auto)
    / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}
.wrapper {
  display: grid;
  grid:
    "hd hd hd hd   hd   hd   hd   hd   hd" minmax(100px, auto)
    "sd sd sd main main main main main main" minmax(100px, auto)
    "ft ft ft ft   ft   ft   ft   ft   ft" minmax(100px, auto)
    / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

自动定位

为了精确地把项目摆放到网格中,CSS 网格布局规范还包含另外一组规则,用来约定当部分或全部子项目没有被明确指定位置时该如何处理。 你会发现针对含有数个项目的网格,实际上最简单的方式就是使用自动定位。

默认定位

如果没有为项目指定位置信息,它们就会把自己摆放在网格中,每个单元格中放一个。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>

Alt text

自动定位的默认规则

调整网格中隐式行的大小

在默认情况下,网格中被自动创建的隐式行的尺寸是自适应大小的,也就是说它们会包含所有属于它们的内容,而不会让内容溢出。

不过我们可以通过 grid-auto-rows 属性来控制它们的大小。为了让所有的行都是 100 像素高,可以像下面这样做:

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
}

Alt text

使用 minmax() 调整行的大小

也可以为 grid-auto-rows 设置 minmax() 值,这会让创建出的行保持一个最小尺寸,而且能够自动加高以适应更多的内容。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: minmax(100px, auto);
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: minmax(100px, auto);
}

Alt text

使用轨道列表调整行的大小

也可以向 grid-auto-rows 属性传入一个轨道列表,行的大小就会按轨道列表重复设置。在下例中,轨道列表声明了一个 100 像素的行和第二个 200 像素的行,当内容很多时网格就会自动创建足够多的隐式行来容纳内容。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: 100px 200px;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: 100px 200px;
}

Alt text

按列自动定位

网格也可以按列来自动定位项目,只要设置 grid-auto-flow 的属性值为 column 即可。此时网格将根据已定义的 grid-template-rows 按列摆放项目,当一列中的项目排满,就继续排到下一列中,或排到一个新创建的隐式列中。 因为显式定义了行轨道,所以列轨道是自适应大小的,同样地,也可以像 grid-auto-rows 那样通过设置 grid-auto-columns 来控制隐式列的尺寸。

在下面的例子中,创建了一个行高为 200 像素的三个行轨道的网格,按列优先自动排列,先创建一个 300 像素宽的列,接着是一个 100 像素宽的列,直至创建出足够多的列来容纳全部项目。

css
.wrapper {
  display: grid;
  grid-template-rows: repeat(3, 200px);
  gap: 10px;
  grid-auto-flow: column;
  grid-auto-columns: 300px 100px;
}
.wrapper {
  display: grid;
  grid-template-rows: repeat(3, 200px);
  gap: 10px;
  grid-auto-flow: column;
  grid-auto-columns: 300px 100px;
}
html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
  <div>Eight</div>
</div>
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
  <div>Eight</div>
</div>

Alt text

网格布局中的对齐

CSS 网格布局实现了盒模型对齐 Level 3 规范,与用于弹性容器中元素对齐的弹性盒的标准相同。这个规范详细约定了在不同的布局方式下如何处理对齐问题。

两条轴线

网格布局方式下共有两条轴线用于对齐——块轴和行轴(内联轴)。块方向的轴是采用块布局时块的排列方向。假设页面中有两个段落,其中一个显示在另一个下面,所以这个方向的轴被称为块轴。

Alt text

行轴与块方向的轴垂直,它的方向和常规内联流中的文本方向一致。

Alt text

我们可以把网格区域中的内容,以及网格轨道整体与这两条轴线对齐。

对齐元素到块轴

属性 align-selfalign-items 用于控制元素在块方向的轴上对齐,通过设置这两个属性,可以改变网格区域中的元素的对齐方式。

使用 align-items

在下面的示例中,网格中包含四个网格区域,可以通过网格容器上的 align-items 属性,把元素的对齐方式设置为下列值之一:

  • auto
  • normal
  • start
  • end
  • center
  • stretch
  • baseline
  • first baseline
  • last baseline
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
  grid-template-areas:
    "a a a a b b b b"
    "a a a a b b b b"
    "c c c c d d d d"
    "c c c c d d d d";
  align-items: start;
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
  grid-template-areas:
    "a a a a b b b b"
    "a a a a b b b b"
    "c c c c d d d d"
    "c c c c d d d d";
  align-items: start;
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>

Alt text

注意,一旦设置了 align-items: start,每个子元素——div 的高度将由 div 中的内容决定。这与省略 align-items(会将每个 div 的高度会被拉伸到网格区域的高度)完全相反。

设置了 align-items 属性,就相当于为网格的所有子元素都设置了 align-self 属性,当然也可以为单独的某一个网格元素设置它的个性化的 align-self 属性。

使用align-self

将 align-self 设置为不同的值的对齐效果。第一个区域的 align-self 采用默认值即 stretch,第二个元素的 align-self 的值为 start,第三个元素的值为 end,第四个元素的值为 center。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
  grid-template-areas:
    "a a a a b b b b"
    "a a a a b b b b"
    "c c c c d d d d"
    "c c c c d d d d";
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
  align-self: start;
}
.item3 {
  grid-area: c;
  align-self: end;
}
.item4 {
  grid-area: d;
  align-self: center;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
  grid-template-areas:
    "a a a a b b b b"
    "a a a a b b b b"
    "c c c c d d d d"
    "c c c c d d d d";
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
  align-self: start;
}
.item3 {
  grid-area: c;
  align-self: end;
}
.item4 {
  grid-area: d;
  align-self: center;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>

Alt text

具有固定宽高比的元素

对 align-self 的默认行为是拉伸(stretch),例外的情况是若元素具有固定宽高比,行为就改为与轴起点对齐(start)。此项例外的原因在于,如果把一个有固定宽高比的元素拉伸,将导致它扭曲变形。

对齐元素到行轴

与 align-items 和 align-self 用于对齐元素到块轴类似,justify-itemsjustify-self 用于对齐元素到行轴,可选值也和 align-self 一样。

  • auto
  • normal
  • start
  • end
  • center
  • stretch
  • baseline
  • first baseline
  • last baseline

借用上面演示过的设置 align-items 的示例,下面把它改为设置 justify-self 属性。

默认的对齐方式仍然是 stretch,除非元素具有固定宽高比。也就是说在默认情况下,网格元素会覆盖网格区域,除非改变它们的对齐方式。下面的例子中,第一个元素演示了默认的对齐效果:

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
  grid-template-areas:
    "a a a a b b b b"
    "a a a a b b b b"
    "c c c c d d d d"
    "c c c c d d d d";
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
  justify-self: start;
}
.item3 {
  grid-area: c;
  justify-self: end;
}
.item4 {
  grid-area: d;
  justify-self: center;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
  grid-template-areas:
    "a a a a b b b b"
    "a a a a b b b b"
    "c c c c d d d d"
    "c c c c d d d d";
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
  justify-self: start;
}
.item3 {
  grid-area: c;
  justify-self: end;
}
.item4 {
  grid-area: d;
  justify-self: center;
}

Alt text

与 align-self 和 align-items 的关系类似,通过为网格容器设置 justify-items 属性,就相当于为所有的元素都设置了 justify-self 属性。

简写属性

place-items 属性是对 align-items 和 justify-items 的简写。

place-self 属性是对 align-self 和 justify-self 的简写。

使元素居于区域正中

通过组合使用 align 和 justify 属性,让元素居于网格区域的正中就变得非常容易了。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  grid-auto-rows: 200px;
  grid-template-areas:
    ". a a ."
    ". a a .";
}
.item1 {
  grid-area: a;
  align-self: center;
  justify-self: center;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  grid-auto-rows: 200px;
  grid-template-areas:
    ". a a ."
    ". a a .";
}
.item1 {
  grid-area: a;
  align-self: center;
  justify-self: center;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
</div>
<div class="wrapper">
  <div class="item1">Item 1</div>
</div>

Alt text

对齐网格轨道到块轴

设想这样一种场景,网格轨道整体占据的空间小于网格容器,那么就可以在容器中对齐网格轨道。

针对块轴和行轴,分别使用 align-content 对齐到块轴,使用 justify-content 对齐到行轴。

place-content 属性则是对 align-content 和 justify-content 的简写。

属性 align-content、justify-content 和 place-content 的可选值都是:

  • normal
  • start
  • end
  • center
  • stretch
  • space-around
  • space-between
  • space-evenly
  • baseline
  • first baseline
  • last baseline

下面的示例中,网格容器的宽高都是 500 像素,分别定义了行轨道和列轨道各 3 条,轨道尺寸为 100 像素,轨道间隙为 10 像素。可知,网格容器的块方向和文字方向都留有多余的空间。

属性 align-content 要设置在网格容器上,因为它的效果应用在整个网格中。

默认对齐方式

css
* {
  box-sizing: border-box;
}

.wrapper {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
}

.wrapper > div {
  border: 2px solid #ffa94d;
  border-radius: 5px;
  background-color: #ffd8a8;
  padding: 1em;
  color: #d9480f;
}
* {
  box-sizing: border-box;
}

.wrapper {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
}

.wrapper > div {
  border: 2px solid #ffa94d;
  border-radius: 5px;
  background-color: #ffd8a8;
  padding: 1em;
  color: #d9480f;
}
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  height: 500px;
  width: 500px;
  gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  height: 500px;
  width: 500px;
  gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>

Alt text

设置 align-content: end

如果把容器中 align-content 的值改为 end,那么网格轨道整体都会移到贴近块方向轴线的最后一条线:

css
.wrapper {
  /* 省略其它属性 */
  align-content: end;
}
.wrapper {
  /* 省略其它属性 */
  align-content: end;
}

Alt text

设置 align-content: space-between

也可以为这个属性设置与弹性盒布局(Flex)方式类似的用于分配空间的值:space-between,space-around 和 space-evenly。比如把 align-content 的值改为 space-between。

css
.wrapper {
  /* 省略其它属性 */
  align-content: space-between;
}
.wrapper {
  /* 省略其它属性 */
  align-content: space-between;
}

Alt text

需要注意,这些与分配空间有关的值会使网格元素变大。如果元素跨越了多于一条轨道,因为轨道的间隙变大了,所以元素也变得更大了。因为精确尺寸的网格较为常用,所以如果你决定使用这些值,一定要确保其中的内容能够适应多出来的空间,或者在使用这些属性值时,把元素的对齐方式设置为对齐到起点(start),可能会比设置为拉伸(stretch)要好。

下图中的网格使用了 align-content 属性,左侧的值为 start,右侧的值为 space-between。观察 item 1 和 item 2,它们都跨越了两条行轨道,右侧的图因为增加了轨道间隙,所以它们占据的空间变大了:

Alt text

对齐网格轨道到行轴

在行轴上使用 justify-content 属性可以获得与在块轴上使用 align-content 相同的对齐效果。

继续利用上面的示例,设置 justify-content 的值为 space-around,那些占据超过一列的元素将因此获得额外的空间:

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  height: 500px;
  width: 500px;
  gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
  align-content: space-between;
  justify-content: space-around;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  height: 500px;
  width: 500px;
  gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
  align-content: space-between;
  justify-content: space-around;
}

Alt text

对齐和自动外边距

在网格区域内使元素对齐的另一个方法是使用自动外边距。如果你曾经用过把容器的左右外边距都设置为 auto 以让页面布局显示在视口中间的方法的话,你肯定知道自动外边距能够消化掉全部的多余空间。当把两侧的外边距都设置为 auto 时,块元素就会被挤到中间,多余的空间则被留到两侧。

在下面的例子中,元素 item 1 的左外边距被设置成 auto,可以看到内容被推到了区域右侧,因为在为元素的内容分配了空间之后,自动外边距占据了剩余的空间:

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  height: 500px;
  width: 500px;
  gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
}
.item1 {
  grid-area: a;
  margin-left: auto;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  height: 500px;
  width: 500px;
  gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
}
.item1 {
  grid-area: a;
  margin-left: auto;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}
html
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>
<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>

Alt text

在浏览器的调试工具中,我们可以清晰的看到元素是如何被对齐的:

Alt text

实现常见布局

我们来用网格布局,实现一些常见的业务场景。

动态列&响应式布局

许多网站都是这种布局的变体,有内容、边栏、页眉和页脚。在响应式设计中,窄屏幕中将布局显示为单个列,到达某个临界宽度后添加侧栏,然后为更宽的屏幕引入三列布局。

Alt text

我们使用命名模板区域来创建这个布局。 创建一个容器,其中包含用于标题、页脚、主要内容、导航、边栏和打算放置广告的块的元素。

html
<div class="wrapper">
  <header class="main-head">The header</header>
  <nav class="main-nav">
    <ul>
      <li><a href="">Nav 1</a></li>
      <li><a href="">Nav 2</a></li>
      <li><a href="">Nav 3</a></li>
    </ul>
  </nav>
  <article class="content">
    <h1>Main article area</h1>
    <p>
      In this layout, we display the areas in source order for any screen less
      that 500 pixels wide. We go to a two column layout, and then to a three
      column layout by redefining the grid, and the placement of items on the
      grid.
    </p>
  </article>
  <aside class="side">Sidebar</aside>
  <div class="ad">Advertising</div>
  <footer class="main-footer">The footer</footer>
</div>
<div class="wrapper">
  <header class="main-head">The header</header>
  <nav class="main-nav">
    <ul>
      <li><a href="">Nav 1</a></li>
      <li><a href="">Nav 2</a></li>
      <li><a href="">Nav 3</a></li>
    </ul>
  </nav>
  <article class="content">
    <h1>Main article area</h1>
    <p>
      In this layout, we display the areas in source order for any screen less
      that 500 pixels wide. We go to a two column layout, and then to a three
      column layout by redefining the grid, and the placement of items on the
      grid.
    </p>
  </article>
  <aside class="side">Sidebar</aside>
  <div class="ad">Advertising</div>
  <footer class="main-footer">The footer</footer>
</div>

因为我们使用grid-template-areas来创建布局。在媒体查询语句之外,需要命名区域。我们使用grid-area 属性命名区域。

css
.main-head {
  grid-area: header;
}
.content {
  grid-area: content;
}
.main-nav {
  grid-area: nav;
}
.side {
  grid-area: sidebar;
}
.ad {
  grid-area: ad;
}
.main-footer {
  grid-area: footer;
}
.main-head {
  grid-area: header;
}
.content {
  grid-area: content;
}
.main-nav {
  grid-area: nav;
}
.side {
  grid-area: sidebar;
}
.ad {
  grid-area: ad;
}
.main-footer {
  grid-area: footer;
}

子元素项目现在有了名称,我们可以使用它来创建默认布局。

css
.wrapper {
  display: grid;
  grid-gap: 20px;
  grid-template-areas:
    "header"
    "nav"
    "content"
    "sidebar"
    "ad"
    "footer";
}
.wrapper {
  display: grid;
  grid-gap: 20px;
  grid-template-areas:
    "header"
    "nav"
    "content"
    "sidebar"
    "ad"
    "footer";
}

在设置了移动布局之后,我们将根据屏幕大小,添加一个媒体查询并重新定义布局,以满足屏幕空间足够显示两列的情况。

css
@media (min-width: 500px) {
  .wrapper {
    grid-template-columns: 1fr 3fr;
    grid-template-areas:
      "header  header"
      "nav     nav"
      "sidebar content"
      "ad      footer";
  }
  nav ul {
    display: flex;
    justify-content: space-between;
  }
}
@media (min-width: 500px) {
  .wrapper {
    grid-template-columns: 1fr 3fr;
    grid-template-areas:
      "header  header"
      "nav     nav"
      "sidebar content"
      "ad      footer";
  }
  nav ul {
    display: flex;
    justify-content: space-between;
  }
}

可以看到布局在 grid-template-areas 的值中成形。 header 和 nav 跨越两个列轨道。在第三行轨道中,我们在 content 旁边设置了 sidebar。在第四行轨道,放置了广告侧边栏, 位于 footer 的左侧。

现在可以添加最后一个断点来适应到三列布局。

css
@media (min-width: 700px) {
  .wrapper {
    grid-template-columns: 1fr 4fr 1fr;
    grid-template-areas:
      "header header  header"
      "nav    content sidebar"
      "nav    content ad"
      "footer footer  footer";
  }
  nav ul {
    flex-direction: column;
  }
}
@media (min-width: 700px) {
  .wrapper {
    grid-template-columns: 1fr 4fr 1fr;
    grid-template-areas:
      "header header  header"
      "nav    content sidebar"
      "nav    content ad"
      "footer footer  footer";
  }
  nav ul {
    flex-direction: column;
  }
}

三列布局有两个 1fr 单元侧列和一个中间列,轨道大小为 4fr。这意味着容器中的可用空间被划分为 6 个部分,并按照我们的三个轨道的比例分配—每个轨道的一个部分位于侧列,四个部分位于中心。

在这个布局中,我在左边的列中显示导航,旁边是内容。在右边栏我们有侧边栏,在它下面是广告。页脚现在横跨布局的底部。

Alt text

上面我们通过添加不同的临界宽度值,来实现了从一列到三列,从窄屏到宽屏的响应式布局设计,这非常适合在产品原型阶段,快速搭建出一个页面UI。

灵活的 12 列布局

许多UI组件库中,都有 12列或 24列的网格布局系统。我们也可以使用 CSS 网格布局创建这种类型的系统。

下面的例子中,我们创建一个 12 列的灵活网格,它有 12 个 1fr宽的 列轨道,且都有一个名为 col-start 的起始行。这意味着我们将拥有 12 条名为 col-start 的网格线。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
  grid-gap: 20px;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
  grid-gap: 20px;
}

在容器中放置 四个 子元素。

html
<div class="wrapper">
  <div class="item1">Start column line 1, span 3 column tracks.</div>
  <div class="item2">
    Start column line 6, span 4 column tracks. 2 row tracks.
  </div>
  <div class="item3">Start row 2 column line 2, span 2 column tracks.</div>
  <div class="item4">
    Start at column line 3, span to the end of the grid (-1).
  </div>
</div>
<div class="wrapper">
  <div class="item1">Start column line 1, span 3 column tracks.</div>
  <div class="item2">
    Start column line 6, span 4 column tracks. 2 row tracks.
  </div>
  <div class="item3">Start row 2 column line 2, span 2 column tracks.</div>
  <div class="item4">
    Start at column line 3, span to the end of the grid (-1).
  </div>
</div>

然后,我可以使用命名行和 span 关键字将它们放到网格上。

css
.item1 {
  grid-column: col-start / span 3;
}
.item2 {
  grid-column: col-start 6 / span 4;
  grid-row: 1 / 3;
}
.item3 {
  grid-column: col-start 2 / span 2;
  grid-row: 2;
}
.item4 {
  grid-column: col-start 3 / -1;
  grid-row: 3;
}
.item1 {
  grid-column: col-start / span 3;
}
.item2 {
  grid-column: col-start 6 / span 4;
  grid-row: 1 / 3;
}
.item3 {
  grid-column: col-start 2 / span 2;
  grid-row: 2;
}
.item4 {
  grid-column: col-start 3 / -1;
  grid-row: 3;
}

因为我们有 12 行名称相同,所以我们使用名称,然后是行索引。我们也没有设置结束行号,而是选择使用 span 关键字表示这个元素应该跨多少个轨道。

Alt text

使用 12 列系统构建布局

我们用 12 列系统创建一个复杂点的例子。

html
<div class="wrapper">
  <header class="main-head">The header</header>
  <nav class="main-nav">
    <ul>
      <li><a href="">Nav 1</a></li>
      <li><a href="">Nav 2</a></li>
      <li><a href="">Nav 3</a></li>
    </ul>
  </nav>
  <article class="content">
    <h1>Main article area</h1>
    <p>
      In this layout, we display the areas in source order for any screen less
      that 500 pixels wide. We go to a two column layout, and then to a three
      column layout by redefining the grid, and the placement of items on the
      grid.
    </p>
  </article>
  <aside class="side">Sidebar</aside>
  <div class="ad">Advertising</div>
  <footer class="main-footer">The footer</footer>
</div>
<div class="wrapper">
  <header class="main-head">The header</header>
  <nav class="main-nav">
    <ul>
      <li><a href="">Nav 1</a></li>
      <li><a href="">Nav 2</a></li>
      <li><a href="">Nav 3</a></li>
    </ul>
  </nav>
  <article class="content">
    <h1>Main article area</h1>
    <p>
      In this layout, we display the areas in source order for any screen less
      that 500 pixels wide. We go to a two column layout, and then to a three
      column layout by redefining the grid, and the placement of items on the
      grid.
    </p>
  </article>
  <aside class="side">Sidebar</aside>
  <div class="ad">Advertising</div>
  <footer class="main-footer">The footer</footer>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
  grid-gap: 20px;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
  grid-gap: 20px;
}

我们继续支持响应式布局,不过这次使用的是命名行。每个断点都将使用一个 12 列的网格,但是,根据屏幕的大小,项目将跨越的轨道数量会发生变化。

我们首先适配移动设备,对于最窄的屏幕,我们想要的只是保持项目的源顺序,并且所有项目都跨越网格。

css
.wrapper > * {
  grid-column: col-start / span 12;
}
.wrapper > * {
  grid-column: col-start / span 12;
}

接下来,我们适配到两列布局。标题和导航仍然跨整个网格,所以我们不需要为它们指定任何位置。侧边栏从第一行 colstart 开始,共 3 行。它在第 3 行之后,标题和导航占据了前两行轨道。

css
@media (min-width: 500px) {
  .side {
    grid-column: col-start / span 3;
    grid-row: 3;
  }
  .ad {
    grid-column: col-start / span 3;
    grid-row: 4;
  }
  .content,
  .main-footer {
    grid-column: col-start 4 / span 9;
  }
  nav ul {
    display: flex;
    justify-content: space-between;
  }
}
@media (min-width: 500px) {
  .side {
    grid-column: col-start / span 3;
    grid-row: 3;
  }
  .ad {
    grid-column: col-start / span 3;
    grid-row: 4;
  }
  .content,
  .main-footer {
    grid-column: col-start 4 / span 9;
  }
  nav ul {
    display: flex;
    justify-content: space-between;
  }
}

最后,适配布局的三列版本。标题继续横跨整个网格,但现在导航将向下移动,成为第一个侧边栏,其中包含内容,然后是旁边的侧边栏。页脚现在也跨整个布局。

css
@media (min-width: 700px) {
  .main-nav {
    grid-column: col-start / span 2;
    grid-row: 2 / 4;
  }
  .content {
    grid-column: col-start 3 / span 8;
    grid-row: 2 / 4;
  }
  .side {
    grid-column: col-start 11 / span 2;
    grid-row: 2;
  }
  .ad {
    grid-column: col-start 11 / span 2;
    grid-row: 3;
  }
  .main-footer {
    grid-column: col-start / span 12;
  }
  nav ul {
    flex-direction: column;
  }
}
@media (min-width: 700px) {
  .main-nav {
    grid-column: col-start / span 2;
    grid-row: 2 / 4;
  }
  .content {
    grid-column: col-start 3 / span 8;
    grid-row: 2 / 4;
  }
  .side {
    grid-column: col-start 11 / span 2;
    grid-row: 2;
  }
  .ad {
    grid-column: col-start 11 / span 2;
    grid-row: 3;
  }
  .main-footer {
    grid-column: col-start / span 12;
  }
  nav ul {
    flex-direction: column;
  }
}

Alt text

卡片列表

许多网站都是是一组“卡片”——产品列表、图像库等等,形成瀑布流的效果。使用网格布局可以非常容易实现此类效果,而且不需要添加媒体查询就可以实现响应式。

容器中的元素是一个无序的项目列表。每个项目都包含一个标题、一些不同高度的文本和 action 链接。

html
<ul class="listing">
  <li>
    <h2>Item One</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li>
    <h2>Item Two</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li class="wide">
    <h2>Item Three</h2>
    <div class="body">
      <p>The content of this listing item goes here.</p>
      <p>This one has more text than the other items.</p>
      <p>Quite a lot more</p>
      <p>Perhaps we could do something different with it?</p>
    </div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li>
    <h2>Item Four</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li>
    <h2>Item Five</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
</ul>
<ul class="listing">
  <li>
    <h2>Item One</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li>
    <h2>Item Two</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li class="wide">
    <h2>Item Three</h2>
    <div class="body">
      <p>The content of this listing item goes here.</p>
      <p>This one has more text than the other items.</p>
      <p>Quite a lot more</p>
      <p>Perhaps we could do something different with it?</p>
    </div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li>
    <h2>Item Four</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
  <li>
    <h2>Item Five</h2>
    <div class="body"><p>The content of this listing item goes here.</p></div>
    <div class="cta"><a href="">Call to action!</a></div>
  </li>
</ul>

我们将创建一个具有灵活数量的灵活列的网格。设定不要小于 200 像素的宽,然后平分剩余空间——所以我们总是得到相同宽度的列轨迹。我们使用 minmax() 函数实现这一点,该函数是用于轨道大小的重复表示法。

css
.listing {
  list-style: none;
  margin: 2em;
  display: grid;
  grid-gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.listing {
  list-style: none;
  margin: 2em;
  display: grid;
  grid-gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

如果让窗口变小或变宽,列跟踪的数量就会发生变化,而不需要使用媒体查询添加断点并重新定义网格。

然后,我们将列表项设置为 display: flex 和 flex-direction 设置为 column。然后,我可以在 .cta 上使用margin-top: auto将这个工具条推到盒子底部。

css
.listing li {
  border: 1px solid #ffe066;
  border-radius: 5px;
  display: flex;
  flex-direction: column;
}
.listing .cta {
  margin-top: auto;
  border-top: 1px solid #ffe066;
  padding: 10px;
  text-align: center;
}
.listing .body {
  padding: 10px;
}
.listing li {
  border: 1px solid #ffe066;
  border-radius: 5px;
  display: flex;
  flex-direction: column;
}
.listing .cta {
  margin-top: auto;
  border-top: 1px solid #ffe066;
  padding: 10px;
  text-align: center;
}
.listing .body {
  padding: 10px;
}

Alt text

使用 dense 关键字避免间隙

当某些卡片的宽度值比较大的时候,占据多个轨道空间,而剩余的行空间不够容纳一个卡片时,便会产生空白间隙。

较大的项目上有一个 wide 类,我们添加了一个样式规则grid-column-end,其值为 span 2。现在,当 grid 遇到这个项目时,它将被分配到两个轨道上。在某些宽度的容器上,就会产生这样的一个缺口,这个缺口没有空间放置一个双轨项目。

Alt text

我可以通过设置grid-auto-flow: dense 在网格容器上设置稠密,从而使网格填充这些空白。但是,在这样做时要小心,因为它会使项目偏离其逻辑原顺序。

css
.listing {
  list-style: none;
  margin: 2em;
  display: grid;
  grid-gap: 20px;
  grid-auto-flow: dense;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.listing .wide {
  grid-column-end: span 2;
}
.listing {
  list-style: none;
  margin: 2em;
  display: grid;
  grid-gap: 20px;
  grid-auto-flow: dense;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.listing .wide {
  grid-column-end: span 2;
}

Alt text

参考